element-ui + axios 上传爬坑之路

前言

最新项目需要实现一个上传 excel 表格的功能,由于项目使用了 element-ui 作为 UI 框架,所以心里满心欢喜的开始使用 el-upload 来上传文件。之后很迅速的根据 element-ui 文档实现了上传的代码:

1
2
3
4
5
6
7
8
9
<el-upload
class="avatar-uploader"
:action="url"
:show-file-list="false"
name="excelfile"
:on-success="handleAvatarSuccess">
<img v-if="imageUrl" :src="imageUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>

果然很完美的上传成功了…

突然发现,咦,需要实现带有进度条的上传,认真翻看了一下 element-ui 的文档,发现并没有什么方法可以快速自定义的实现上传的进度条,看来只能使用自定义上传了。

使用 el-upload 自定义上传文件

很快速的使用 :before-upload=”beforeAvatarUpload” 自定义了上传方法,方法代码很简单,这里利用 FromData 对象进行上传,都知道要设置headers中的 content-type 为 multipart/form-data:

1
2
3
4
5
6
7
8
axios.post(url, fd, {
data: fd,
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(res => {
console.log(res)
})

先测试了一下上传能不能成功~,结果发现,居然失败了:

到这里,又尝试的修改了一些参数的传递方法等各种,最后都失败了,结果差不多….

尝试用原生的 input 来上传文件

由于尝试了很多次,都没有成功,不知道哪里出了问题,所以开始尝试上传方法 beforeAvatarUpload 里用原生的 ajax 来上传文件, 大致代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
let xhr = new XMLHttpRequest()
xhr.open('post', url, true)
xhr.upload.addEventListener('progress', function (event) {
if (event.lengthComputable) {
let percentComplete = event.loaded / event.total
console.log(percentComplete)
}
}, false)
xhr.onload = function (res) {
console.log(res)
vm.uploadLoading = false
vm.$message.success('上传成功')
vm.$emit('updatePayment')
}
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
let data = xhr.response
if (data.ok === 1) {
vm.uploadLoading = false
vm.$message.success('上传成功')
} else if (data.ok === 0 && data.exist === 1) {
vm.uploadLoading = false
vm.$message.warning('已存在同文件名文件')
} else if (data.ok === 0 && data.exist === 0){
vm.$message.warning(data.msg)
}
} else {
vm.$message.error('上传失败,请检查文件')
}
}
}
xhr.send(fd)

结果是: 没有问题,上传成功了…

到这里,可以说明是 axios 这边产生了某些问题了。 马上开始搜索解决方案,找到了一个解决思路,就是:axios拦截了请求,对请求的数据做了一些处理,而 FormData 传输文件不需要任何的处理。所以就在想直接在 axios 上过载一个方法,躲过 axios 的拦截。

解决问题

在仔细的查看 axios 文档后,发现文档中有一个创建实例 (Creating an instance) 的方法 axios.create([config]) ,最终解决问题,代码大致为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// 上传报表操作
beforeAvatarUpload (file) {
let vm = this
vm.uploadFileNum++
const fileName = file.name.split('.')[1]
if (fileName === 'xlsx' || fileName === 'xls' || fileName === 'csv') {
let url = 'ajax/forms.php?cmd=upload_excel'
let fd = new FormData()
fd.append('excelfile', file)
fd.append('lid', this.loginMessage.lid)
fd.append('sn', this.loginMessage.sn)
if (vm.isUploading) {
vm.uploadingQueue.push(file)
let obj = {}
obj.fileName = file.name
obj.uname = vm.loginMessage.uname
obj.time = (new Date()).valueOf() / 1000
obj.uploading = true
if (vm.uploadDataList.length === 0) {
vm.$emit('virtualAdd', true)
}
vm.uploadDataList.unshift(obj)
vm.uploadFileNum--
return
}
vm.uploadLoading = true
// let url = 'ajax/forms.php?cmd=upload_excel' + '&lid=' + this.loginMessage.lid + '&sn=' + this.loginMessage.sn
// let fd = new FormData()
// fd.append('excelfile', file)
vm.isUploading = true
axios.create({
headers: {
'Content-Type': 'multipart/form-data'
},
onUploadProgress: function (event) { // 上传进度
console.log(event)
if (vm.uploadFileNum > 0) {
let obj = {}
obj.fileName = file.name
obj.uname = vm.loginMessage.uname
obj.time = (new Date()).valueOf() / 1000
obj.uploading = true
vm.uploadDataList.unshift(obj)
if (!vm.isShowTable && vm.uploadFileNum === 1) {
vm.$emit('virtualAdd', true)
}
vm.uploadFileNum--
}
vm.uploadLoading = false
vm.$emit('uploadFile', true)
}
}).post(url, fd).then(res => {
console.log(res)
if (res.data.ok === 1) {
if (this.uploadingQueue.length === 0) {
this.$message.success('上传成功')
} else { // 如果待上传的队列不为空(前端实现多文件持续上传)
// 更新报表
// 从待上传队列中弹出一个来继续上传
this.$emit('updatePayment', 1, 'isSuccess')
let nextFile = this.uploadingQueue[0]
this.continueUploading(url, nextFile)
return
}
} else if (res.data.ok === 0 && res.data.exist === 1) {
this.$message.warning('已存在同文件名文件:' + file.name)
} else {
let msg = res.data.msg
if (msg) {
let code = Math.abs(res.data.code)
this.errorCodeDispose(msg, code)
} else {
this.$message.error('系统错误')
}
}
if (this.uploadingQueue.length !== 0) {
// 从待上传队列中弹出一个来继续上传
this.$emit('updatePayment', 1, 'isSuccess')
let nextFile = this.uploadingQueue[0]
this.continueUploading(url, nextFile)
} else {
// 取消正在上传的状态
this.$emit('uploadFile', false)
// 更新报表
this.$emit('updatePayment')
this.isUploading = false
}
})
return false
} else {
this.$message.warning('上传文件格式必须为xlsx,xls或者csv')
}
},
// 队列继续上传方法
continueUploading (url, fd) {
// console.log('继续上传')
let nextFd = new FormData()
nextFd.append('excelfile', fd)
nextFd.append('lid', this.loginMessage.lid)
nextFd.append('sn', this.loginMessage.sn)
axios.create({
headers: {
'Content-Type': 'multipart/form-data'
}
}).post(url, nextFd).then(res => {
console.log(res)
if (res.data.ok === 1) {
this.uploadingQueue.shift()
if (this.uploadingQueue.length === 0) {
this.$message.success('上传成功')
} else {
// 从待上传队列中弹出一个来继续上传
// 更新报表
this.$emit('updatePayment', 1, 'isSuccess')
let nextFile = this.uploadingQueue[0]
this.continueUploading(url, nextFile)
return
}
} else if (res.data.ok === 0 && res.data.exist === 1) {
this.$message.warning('已存在同文件名文件:' + fd.name)
} else {
let msg = res.data.msg
if (msg) {
let code = Math.abs(res.data.code)
this.errorCodeDispose(msg, code)
} else {
this.$message.error('系统错误')
}
}
this.uploadingQueue.shift()
if (this.uploadingQueue.length !== 0) {
// 从待上传队列中弹出一个来继续上传
this.$emit('updatePayment', 1, 'isSuccess')
let nextFile = this.uploadingQueue[0]
this.continueUploading(url, nextFile)
} else {
// 取消正在上传的状态
this.$emit('uploadFile', false)
// 更新报表
this.$emit('updatePayment')
this.isUploading = false
}
})
}

有些同学可能不知道上传文件为什么要设置headers中的 content-type 为 multipart/form-data,大家可以看看这篇文章:
HTTP请求中 request payload 和 formData 区别?